package com.alipay.account.service;


import com.alibaba.common.lang.StringUtil;
import com.alipay.account.dal.dao.UserAccountDAO;
import com.alipay.account.dal.domain.UserAccountDO;
import com.alipay.account.facade.AccountTransService;
import com.alipay.account.facade.com.alipay.account.dto.AccountDTO;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.request.AlipayUserInfoShareRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayUserInfoShareResponse;
import com.alipay.sofa.dtx.client.aop.annotation.DtxTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

public class AccountTransServiceImpl implements AccountTransService {
    protected final static Logger logger = LoggerFactory.getLogger(AccountTransServiceImpl.class);

    @Autowired
    private UserAccountDAO userAccountDAO;

    @Autowired
    private UserAccountDAO userAccountDAO_02;

    private TransactionTemplate transactionTemplate;

    @Autowired
    private DynamicConfig dynamicConfig;

    public String addAccount(final String authCode) {
        final UserAccountDO userAccountDO = reverse(getUserInfo(authCode));
//        final UserAccountDO userAccountDO = reverse(getMockUserInfo(authCode));

        /*初始金额设置为1w*/
        userAccountDO.setAmount(10000l);
        boolean execRes = (boolean) transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                try {
                    UserAccountDAO buyerUserAccountDAO = getUserAccountDAO(userAccountDO.getUserId());
                    UserAccountDO user = buyerUserAccountDAO.getUserAccountByUid(userAccountDO.getUserId());
                    /*已经存在则直接返回*/
                    if(null != user)
                        return true;
                    UserAccountDAO userAccountDAO = getUserAccountDAO(userAccountDO.getUserId());
                    userAccountDAO.addUser(userAccountDO);
                    return true;
                } catch (Exception e) {
                    logger.error("添加账户信息失败", e);
                    status.setRollbackOnly();
                    throw e;
                }
            }
        });
        if(execRes){
            return userAccountDO.getUserId();
        }else
            throw new RuntimeException("添加账户信息失败");
    }

    @Override
    public boolean doAccount(final String buyerUid, final String sellerUid, final Long transAmount) {
        try {

            checkAmount(transAmount);

            return (boolean) transactionTemplate.execute(new TransactionCallback<Object>() {
                @Override
                public Object doInTransaction(TransactionStatus status) {
                    try {
                        UserAccountDAO buyerUserAccountDAO = getUserAccountDAO(buyerUid);
                        //读取并锁定买方用户账户
                        UserAccountDO userAccountDO = buyerUserAccountDAO.getUserAccountByUidForUpdate(buyerUid);

                        //检查买方用户资金
                        if (userAccountDO.getAmount() < transAmount) {
                            logger.error("用户资金不足");
                            status.setRollbackOnly();
                            //注意, 要回滚分布式事务, 必须抛出异常
                            throw new RuntimeException("用户资金不足");
                        }

                        //扣除买方用户资金
                        userAccountDO.setAmount(userAccountDO.getAmount() - transAmount);
                        //更新
                        buyerUserAccountDAO.updateUserAccount(userAccountDO);

                        UserAccountDAO sellerUserAccountDAO = getUserAccountDAO(sellerUid);
                        //卖方用户增加资金
                        sellerUserAccountDAO.addUserAmount(sellerUid, transAmount);
                        return true;
                    } catch (Exception e) {
                        logger.error("记账操作执行失败", e);
                        status.setRollbackOnly();
                        throw e;
                    }
                }
            });
        } catch (Exception e) {
            logger.error("记账操作执行失败", e);
            //注意, 要回滚分布式事务, 必须抛出异常
            throw e;
        }
    }


    @Override
    public AccountDTO getAccount(String userId, String authCode){
        String uid;
        if(StringUtil.isNotEmpty((userId))){
            uid = userId;
        }else if(StringUtil.isNotEmpty(authCode)){
            uid = getUserInfo(authCode).getUserId();
        }else {
            return new AccountDTO();
        }
        UserAccountDAO buyerUserAccountDAO = getUserAccountDAO(uid);
        UserAccountDO userAccountDO = buyerUserAccountDAO.getUserAccountByUid(uid);
        return convrt(userAccountDO);
    }

    @Override
    public List<AccountDTO> getAllAccounts(){
        List<UserAccountDO> userAccountDOs = userAccountDAO.getAllUserAccounts();
        userAccountDOs.addAll(userAccountDAO_02.getAllUserAccounts());
        final List<AccountDTO> accountDTOs = new ArrayList<>();
        if(!CollectionUtils.isEmpty(userAccountDOs)){
            for(UserAccountDO userAccountDO: userAccountDOs){
                accountDTOs.add(convrt(userAccountDO));
            }
        }
        return accountDTOs;
    }

    public TransactionTemplate getTransactionTemplate() {
        return transactionTemplate;
    }

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    private UserAccountDAO getUserAccountDAO(String userId) {
        Integer shard = Integer.valueOf(userId.substring(userId.length()-2, userId.length()));
        if (shard < 50) {
            return this.userAccountDAO;
        } else {
            return this.userAccountDAO_02;
        }
    }

    private AccountDTO getMockUserInfo(String authCode){
        AccountDTO accountDTO = new AccountDTO();
        accountDTO.setUserId("2088102104794936");
        accountDTO.setNickName("测试用户4936");
        return accountDTO;
    }

    private AccountDTO getUserInfo(String authCode){

        final String pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqWs76xkOmTj0IP5Mn1vmV9+B9ikzEqndjqGKl+q+MxzzBKYSWGvthRUmRU8QKEk1z3p38o8Xkpvczzrn7zamQMUVthUdJfoXsAB9k+WN9EAKCcyS7l39yuLFSXcTLQg78suSOcQwowzdzXXuMPZ64rrKaqG4TcTNASyP7JPg2CPdN+X+XLo2BJwuv8hi7U3M0KAZ5Dk2dJ2nAVxYhRBzauKldiVCre69ceIh0VCB9HkUjqXgcegAYKjhfX80hJSv6Bt0mDqhc1CpUCEB8WPBsqn8d/hQ2K8Nh5YA88tzpRXr3z833LN8uKKbNS0bpUZ7nW2w3BwYXjp/Ka29l7mPbwIDAQAB";
        final String priKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCWX7qHkuljVji81XEAdn1rDBsVrsG3twjEscYZTaCEfwVm4F3V/KiJJSBU663x3nKG5pc7bQC44956pW9DCrsBohhNTzgMVDDNXkSpCFXt9TMSOhPD5IpAIo7osQ1Fu6GSIliV/3hz+/KqJIFIeIUT2Kh1FKjpR1xYPb/FV3PjJtn5hiXQdRTh92LTYbE1kEBZsULDOx09jAEco2eXhi5LtWWeA7ZQdon3Q7jQy6XXueSoZb2gdbHqXb5xgeL35h/XTnvQ+qXpxCO8e37GaI5GI80QQ23o1xRwO2jvDGhcm08MJX5yq2aVwq4D6avZLohdWF5UlGDkLKVWzmSGMHcdAgMBAAECggEAasDfLWxQoCMP+I8WwBtegmZ/u+7qu8AHS4xhdIk1kZqTfSt/snDCPUrMHz2JqZIggECn0JpbzuaGhU95I4E0rb4jvo6k3avPUZzA5bdzOZG/hgsRCRrwQwPFrNA0+fJwi8wyU5qgKscMyVlNJuPYpfRD84qNN79fgY2k3TZuoS1Whle1b8dG5krax/9UUBu5Nhi0fI/F/8C/rXL9i8IryAqRgmO6LqSGr+2eqRHK+hWUV+FVxOepW5q4umQUGpiIHyQ+7Q9M6c+m6holbp7SJggE8cccVtdkYfSh8nvE0nlRPyknF60QM6KLz0zOZDtpvMK5m/jAs9/GAY7IgKtuCQKBgQDSQXWJ0NIwxTXTt4FtpqMZ1Jci6QQSeJnRWoc674ncV+dTFRUMaqEzgOfsFB+D56FYwcaX/NHqT1HUxtCtiR4RBhK7rIqsfQufhTb1ba7H2j0YcibmvOjF9VUORBXVl0cjfho/FAJ3YY17XcMabX5pDwXsEJu2lEXoLzztIIuCswKBgQC3Fw4oFDYCANEXc4FK4SQ7c6CQfQ9EszxLbwhcVcLEwx5i7eG1RkYVY7+FK5ikprQBkKT+DeoQjFU8DVVAZo67IQ8J2t7S5DdzVhBSftVN6ptzYitI14FMnOYAp7JFhzfS5jBG0LjCejcHFSljDXToHe6qZWmOyUHm0lgh0TnG7wKBgBT7S0txd0g+pdx214eltWkCegKV1j4xosftjMko1k0SyJbV57/RdDy7hGleqLZ1CvjcfYD1Tc1gFP5yyKhIoqN4Q65OzZfg8qP8mztqsqJm+OnTmmY0O9ZlYROHX9ZsvkCgeOYKjSR0q7W/6ZRdkNvTD/g6wA88RK2i6SWQtdtZAoGAYwcmUgk0ePrmHPANc2m4xisxIlVyB6+GIirI8+Ay+2eprMqLdRewWmtcMHQ3GH9OBzar9vmhqxesFlID1VRjSNWFo8ftE1X6o/hd1bDiQEVvlQX48WoWdlC6mooZgz+ysvBsFjFYbV/bGYqZmwixPcbKywEVCfTTHzyQDKJ0xKMCgYEAl1+HpFfLnoSnnunSI5YejNwNP/DRfAeHyJ4APakmI8OsDUkoK290SRsbl3GRTD05AxTLKSSaH5L+KEIHFEL7WuppXUVn3QDkkxx3JQwfdsO2UZDZSoQSYYhXr6mjilD24kUHaQ/S4pGwI7k185SUNylJPzt1rzyN0mSAPB8lWF4=";
        final String appId = "2018090461265312";


        AccountDTO accountDTO = new AccountDTO();
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", appId, priKey, "json", "utf-8", pubKey, "RSA2");
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        request.setGrantType("authorization_code");
        request.setCode(authCode);
//        request.setRefreshToken("201208134b203fe6c11548bcabd8da5bb087a83b");
        AlipaySystemOauthTokenResponse response;
        try {
            response = alipayClient.execute(request);
        }catch (Exception e){
            logger.error("auth user from openapi meets except",e);
            throw new RuntimeException(e);
        }
        if (response.isSuccess()) {
            accountDTO.setUserId(response.getUserId());
        } else {
            throw new RuntimeException("auth user from openapi failed!");
        }

        AlipayUserInfoShareRequest requestInfo = new AlipayUserInfoShareRequest();
        AlipayUserInfoShareResponse responseInfo;
        try {
            responseInfo = alipayClient.execute(requestInfo, response.getAccessToken());
        }catch (Exception e){
            logger.error("get user info from openapi meets except",e);
            throw new RuntimeException(e);
        }
        if(responseInfo.isSuccess()){
            accountDTO.setNickName(responseInfo.getNickName());
        } else {
            throw new RuntimeException("get user info from openapi failed!");
        }

        return accountDTO;
    }

    private void checkAmount(Long transAmount){
        Integer maxTransAccount = dynamicConfig.getMaxTransAmount();
        if(null != maxTransAccount && transAmount > maxTransAccount){
            throw new RuntimeException("转账超过限额["+maxTransAccount+"]!");
        }
    }

    private AccountDTO convrt(UserAccountDO userAccountDO){
        AccountDTO accountDTO = new AccountDTO();
        if(null == userAccountDO)
            return accountDTO;
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        accountDTO.setUserId(userAccountDO.getUserId());
        accountDTO.setAmount(userAccountDO.getAmount());
        accountDTO.setGmtCreate(dateFormat.format(userAccountDO.getGmtCreate()));
        accountDTO.setAmountLimit(dynamicConfig.getMaxTransAmount());
        accountDTO.setNickName(userAccountDO.getNickName());
        Integer shard = Integer.valueOf(accountDTO.getUserId().substring(accountDTO.getUserId().length()-2, accountDTO.getUserId().length()));
        if (shard < 50) {
            accountDTO.setDbShardNum("01");
        } else {
            accountDTO.setDbShardNum("02");
        }
        return accountDTO;
    }

    private UserAccountDO reverse(AccountDTO accountDTO){
        UserAccountDO userAccountDO = new UserAccountDO();
        userAccountDO.setUserId(accountDTO.getUserId());
        userAccountDO.setNickName(accountDTO.getNickName());
        return userAccountDO;
    }
}
